package com.ezandroid.alphago;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Spinner;

import com.ezandroid.alphago.board.DefaultBoardModel;
import com.ezandroid.alphago.board.GoBoard;
import com.ezandroid.alphago.board.GoException;
import com.ezandroid.alphago.board.GoPoint;
import com.ezandroid.alphago.board.IGridListener;
import com.ezandroid.alphago.engine.Debug;
import com.ezandroid.alphago.engine.Feature48;
import com.ezandroid.alphago.engine.History;
import com.ezandroid.alphago.engine.Intersection;
import com.ezandroid.alphago.engine.Move;
import com.ezandroid.alphago.engine.PolicyNN;
import com.ezandroid.alphago.engine.Stone;
import com.ezandroid.alphago.engine.StoneColor;
import com.ezandroid.alphago.game.Game;
import com.ezandroid.alphago.util.AppConstants;

import java.util.HashSet;
import java.util.Set;

/**
 * @author like
 */
public class ManualActivity extends AppCompatActivity implements IGridListener {

    private static final String TAG = "ManualActivity";

    private ViewPager mManualPager;
    private ManualPagerAdapter mManualPagerAdapter;

    private GoBoard mGoBoard;
    private DefaultBoardModel mBoardModel;

    private PolicyNN mPolicyNN;
    private Feature48 mBoard;

    private Game mGame;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_manual);

        initView();
        showNewAIGameDialog(this);
    }

    private void showNewAIGameDialog(final Context context) {
        final View view = LayoutInflater.from(context).inflate(
                R.layout.dialog_ai_new_game, null);
        final Spinner levelSpinner = view.findViewById(R.id.level_spn);
        levelSpinner.setSelection(AppConstants.DEFAULT_GNUGO_LEVEL - 1);
        levelSpinner.setEnabled(false);
        final Spinner komiSpinner = view.findViewById(R.id.komi_spn);
        komiSpinner.setSelection(AppConstants.DEFAULT_KOMI);
        komiSpinner.setEnabled(false);
        final Spinner handicapSpinner = view.findViewById(R.id.handicap_spn);
        handicapSpinner.setSelection(AppConstants.DEFAULT_HANDICAP);
        final Spinner colorSpinner = view.findViewById(R.id.color_spn);
        colorSpinner.setSelection(AppConstants.DEFAULT_COLOR);
        new AlertDialog.Builder(context).setTitle(R.string.fight_dialog_create_game_title)
                .setCancelable(false)
                .setIcon(R.mipmap.ic_launcher).setView(view)
                .setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // 黑白
                        boolean useBlack = colorSpinner.getSelectedItemPosition() ==
                                AppConstants.COLOR_BLACK;
                        // 贴目
                        float komi = 7.5f;
                        switch (komiSpinner.getSelectedItemPosition()) {
                            case AppConstants.KOMI_0:
                                komi = 0;
                                break;
                            case AppConstants.KOMI_6_5:
                                komi = 6.5f;
                                break;
                            case AppConstants.KOMI_7_5:
                                komi = 7.5f;
                                break;
                        }
                        // 让子
                        int handicap = 0;
                        int selectedHandicap = handicapSpinner.getSelectedItemPosition();
                        if (selectedHandicap > 0) {
                            handicap = selectedHandicap + 1;
                        }
                        // 级别
                        int level = levelSpinner.getSelectedItemPosition() + 1;

                        Game game = new Game();
                        game.handicap = handicap;
                        game.level = level;
                        game.komi = komi;
                        game.useBlack = useBlack;

                        mGame = game;

                        if (handicap > 0) {
                            int[] handicapPosArray = Game.HANDICAPS[handicap - 2];
                            for (int pos : handicapPosArray) {
                                {
                                    GoPoint black = new GoPoint();
                                    black.setPlayer(GoPoint.PLAYER_BLACK);
                                    black.setCol(pos % 19);
                                    black.setRow(pos / 19);
                                    mBoard.playMove(pos % 19, pos / 19, Feature48.BLACK);
                                }
                                Stone stone = new Stone();
                                stone.color = StoneColor.BLACK;
                                stone.intersection = new Intersection(pos % 19, pos / 19);
                                Set<Stone> captured = new HashSet<>();
                                Move move = new Move(stone, captured);
                                placeMove(move);
                            }
                        }

                        if (useBlack && handicap > 0) { // 计算机执白，让子>0，白棋先行
                            byte[][] cnn = mBoard.generateFeatures();
                            float[] rate = mPolicyNN.getOutput(cnn);
                            Debug.printRate(rate);
                            int maxPos = findMaxRatePos(rate);
                            int x = maxPos % 19;
                            int y = maxPos / 19;
                            {
                                GoPoint white = new GoPoint();
                                white.setPlayer(GoPoint.PLAYER_WHITE);
                                white.setCol(x);
                                white.setRow(y);
                                mBoard.playMove(x, y, Feature48.WHITE);
                            }
                            History<Move> history = mBoard.getMoveManager().getHistory();
                            Move whiteMove = history.readLatest();
                            placeMove(whiteMove);
//                        } else if (useBlack && handicap == 0) { // 计算机执白，让子=0，黑棋先行
//                        } else if (!useBlack && handicap > 0) { // 计算机执黑，让子>0，白棋先行
                        } else if (!useBlack && handicap == 0) { // 计算机执黑，让子=0，黑棋先行
                            byte[][] cnn = mBoard.generateFeatures();
                            float[] rate = mPolicyNN.getOutput(cnn);
                            Debug.printRate(rate);
                            int maxPos = findMaxRatePos(rate);
                            int x = maxPos % 19;
                            int y = maxPos / 19;
                            {
                                GoPoint black = new GoPoint();
                                black.setPlayer(GoPoint.PLAYER_BLACK);
                                black.setCol(x);
                                black.setRow(y);
                                mBoard.playMove(x, y, Feature48.BLACK);
                            }
                            History<Move> history = mBoard.getMoveManager().getHistory();
                            Move blackMove = history.readLatest();
                            placeMove(blackMove);
                        }
                    }
                }).setNegativeButton(R.string.cancel, null).show();
    }

    public void onResume() {
        super.onResume();
        updateUI();
    }

    public void onPause() {
        super.onPause();
    }

    private void updateUI() {
        if (mGoBoard != null) {
            mGoBoard.updateGoBoardOption();
            mGoBoard.postInvalidate();
        }
    }

    private boolean initBoard() {
        mPolicyNN = new PolicyNN(this);
        mBoard = new Feature48();

        int size = 19;
        DisplayMetrics dm = getResources().getDisplayMetrics();
        // 棋盘大小
        int boardSize = Math.min(dm.widthPixels, dm.heightPixels);
        // 棋子大小
        int cubicSize = Math.round(boardSize * 1.0f / (size + 1));

        mBoardModel = new DefaultBoardModel(size);
        mGoBoard = new GoBoard(this, mBoardModel, cubicSize, cubicSize / 2, cubicSize / 2);
        mGoBoard.setFocusable(true);
        mGoBoard.setClickable(true);
        mGoBoard.setFocusableInTouchMode(true);
        mGoBoard.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mGoBoard.pointerReleased(Math.round(event.getX()), Math.round(event.getY()));
                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    mGoBoard.pointerPressed(Math.round(event.getX()), Math.round(event.getY()));
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    mGoBoard.pointerMoved(Math.round(event.getX()), Math.round(event.getY()));
                }
                return false;
            }
        });
        mGoBoard.setGridListener(this);

        mManualPagerAdapter = new ManualPagerAdapter();
        mManualPagerAdapter.addChild(mGoBoard);
        mManualPager.setAdapter(mManualPagerAdapter);

        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(boardSize, boardSize);
        params.gravity = Gravity.CENTER;
        mManualPager.setLayoutParams(params);
        return true;
    }

    private void initView() {
        mManualPager = findViewById(R.id.board_layout);

        Debug.printPos();
        findViewById(R.id.next_step).setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mIsWaiting) {
                    return;
                }
                for (int i = 0; i < 361; i++) {
                    int mx = i % 19;
                    int my = i / 19;
                    GoPoint point = mBoardModel.getPoint(mx, my);
                    point.setLabel("");
                }
                long time = System.currentTimeMillis();
                Move move1 = mBoard.redo();
                if (move1 != null) {
                    placeMove(move1);
                    Move move2 = mBoard.redo();
                    if (move2 != null) {
                        placeMove(move2);
                    }
                }
                Log.e(TAG, "Redo" + (System.currentTimeMillis() - time));
            }
        });
        findViewById(R.id.prev_step).setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mIsWaiting) {
                    return;
                }
                if (mBoard.getMoveManager().getHistory().getHead() == 0 && !mGame.useBlack) {
                    return;
                }
                for (int i = 0; i < 361; i++) {
                    int mx = i % 19;
                    int my = i / 19;
                    GoPoint point = mBoardModel.getPoint(mx, my);
                    point.setLabel("");
                }
                long time = System.currentTimeMillis();
                Move move1 = mBoard.undo();
                if (move1 != null) {
                    reverseMove(move1);
                    Move move2 = mBoard.undo();
                    if (move2 != null) {
                        reverseMove(move2);
                    }
                }
                Log.e(TAG, "Undo" + (System.currentTimeMillis() - time));
            }
        });
        findViewById(R.id.first_step).setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
            }
        });
        findViewById(R.id.last_step).setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
            }
        });

        findViewById(R.id.scroll_manual).scrollTo(0, 0);

        initBoard();
    }

    @Override
    public void touchPressed(int col, int row) {
        mGoBoard.setTouchPosition(col, row);
        mGoBoard.postInvalidate();
    }

    private boolean mIsWaiting;

    private static int findMaxRatePos(float[] rate) {
        float maxRate = 0;
        int maxPos = -1;
        for (int i = 0; i < rate.length; i++) {
            if (maxRate < rate[i]) {
                maxRate = rate[i];
                maxPos = i;
            }
        }
        return maxPos;
    }

    private void reverseMove(Move move) {
        try {
            mBoardModel.performRemove(move.getStone().intersection.x,
                    move.getStone().intersection.y);
        } catch (GoException e) {
            e.printStackTrace();
        }
        for (Stone stone : move.getCaptured()) {
            GoPoint goPoint = new GoPoint();
            goPoint.setPlayer(stone.color == StoneColor.BLACK ?
                    GoPoint.PLAYER_BLACK : GoPoint.PLAYER_WHITE);
            goPoint.setCol(stone.intersection.x);
            goPoint.setRow(stone.intersection.y);
            goPoint.setNumber(stone.number + 1);
            try {
                mBoardModel.performPut(stone.intersection.x, stone.intersection.y, goPoint);
            } catch (GoException e) {
                e.printStackTrace();
            }
        }
        mGoBoard.postInvalidate();
    }

    private void placeMove(Move move) {
        GoPoint goPoint = new GoPoint();
        goPoint.setPlayer(move.getStone().color == StoneColor.BLACK ?
                GoPoint.PLAYER_BLACK : GoPoint.PLAYER_WHITE);
        goPoint.setCol(move.getStone().intersection.x);
        goPoint.setRow(move.getStone().intersection.y);
        goPoint.setNumber(move.getStone().number + 1);
        try {
            mBoardModel.performPut(move.getStone().intersection.x,
                    move.getStone().intersection.y, goPoint);
        } catch (GoException e) {
            e.printStackTrace();
        }
        for (Stone stone : move.getCaptured()) {
            try {
                mBoardModel.performRemove(stone.intersection.x, stone.intersection.y);
            } catch (GoException e) {
                e.printStackTrace();
            }
        }
        mGoBoard.postInvalidate();
    }

    @Override
    public void touchReleased(final int col, final int row) {
        if (mIsWaiting) {
            return;
        }
        mIsWaiting = true;
        mGoBoard.setTouchPosition(-1, -1);
        new Thread() {

            public void run() {
                long time = System.currentTimeMillis();
                History<Move> history = mBoard.getMoveManager().getHistory();
                float[] rate;
                if (mGame.useBlack) {
                    boolean canMove;
                    {
                        GoPoint black = new GoPoint();
                        black.setPlayer(GoPoint.PLAYER_BLACK);
                        black.setCol(col);
                        black.setRow(row);
                        canMove = mBoard.playMove(col, row, Feature48.BLACK);
                        Log.e(TAG, "Play Black:" + (System.currentTimeMillis() - time));
                    }
                    if (!canMove) {
                        mIsWaiting = false;
                        return;
                    }

                    Move blackMove = history.readLatest();
                    placeMove(blackMove);

                    time = System.currentTimeMillis();
                    byte[][] cnn = mBoard.generateFeatures();
                    rate = mPolicyNN.getOutput(cnn);
                    Log.e(TAG, "Policy UseTime:" + (System.currentTimeMillis() - time));
                    Debug.printRate(rate);
                    int maxPos = findMaxRatePos(rate);
                    int x = maxPos % 19;
                    int y = maxPos / 19;

                    {
                        GoPoint white = new GoPoint();
                        white.setPlayer(GoPoint.PLAYER_WHITE);
                        white.setCol(x);
                        white.setRow(y);
                        time = System.currentTimeMillis();
                        mBoard.playMove(x, y, Feature48.WHITE);
                        Log.e(TAG, "Play White:" + (System.currentTimeMillis() - time));
                    }

                    Move whiteMove = history.readLatest();
                    placeMove(whiteMove);
                } else {
                    boolean canMove;
                    {
                        GoPoint white = new GoPoint();
                        white.setPlayer(GoPoint.PLAYER_WHITE);
                        white.setCol(col);
                        white.setRow(row);
                        time = System.currentTimeMillis();
                        canMove = mBoard.playMove(col, row, Feature48.WHITE);
                        Log.e(TAG, "Play White:" + (System.currentTimeMillis() - time));
                    }
                    if (!canMove) {
                        mIsWaiting = false;
                        return;
                    }

                    Move whiteMove = history.readLatest();
                    placeMove(whiteMove);

                    time = System.currentTimeMillis();
                    byte[][] cnn = mBoard.generateFeatures();
                    rate = mPolicyNN.getOutput(cnn);
                    Log.e(TAG, "Policy UseTime:" + (System.currentTimeMillis() - time));
                    Debug.printRate(rate);
                    int maxPos = findMaxRatePos(rate);
                    int x = maxPos % 19;
                    int y = maxPos / 19;

                    {
                        GoPoint black = new GoPoint();
                        black.setPlayer(GoPoint.PLAYER_BLACK);
                        black.setCol(x);
                        black.setRow(y);
                        mBoard.playMove(x, y, Feature48.BLACK);
                        Log.e(TAG, "Play Black:" + (System.currentTimeMillis() - time));
                    }

                    Move blackMove = history.readLatest();
                    placeMove(blackMove);
                }

                for (int i = 0; i < 361; i++) {
                    int mx = i % 19;
                    int my = i / 19;
                    GoPoint point = mBoardModel.getPoint(mx, my);
                    if (rate[i] > 0.01) {
                        point.setLabel("" + Math.round(rate[i] * 1000 / 10f));
                    } else {
                        point.setLabel("");
                    }
                }

                mGoBoard.postInvalidate();
                mIsWaiting = false;
            }
        }.start();
    }

    @Override
    public void touchMoved(int col, int row) {
        mGoBoard.setTouchPosition(col, row);
        mGoBoard.postInvalidate();
    }
}
